package pers.vic.boot.security.jwt;

import java.util.Date;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.util.Assert;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * @description: JWT 加密，校验工具
 * @author: Vic.xu
 * @date: 2019年12月31日 上午11:19:14
 */
public class JwtUtil {

	// 过期时间X分钟
	private static final long EXPIRE_TIME = Long.valueOf(Integer.MAX_VALUE);//60 * 60 * 1000;

	// 开始检测刷新token的时机
	private static final long EXPIRE_CHECK = 30 * 60 * 1000;

	private static ObjectMapper objectMapper = new ObjectMapper();

	private static final String USERID = "userId";

	private static final String USERNAME = "userName";

	private JwtUtil() {
		throw new IllegalStateException("Utility class");
	}

	/**
	 * 校验token是否正确,利用token中的USERNAME作为密钥
	 *
	 * @param token 密钥
	 */
	public static boolean verify(String token) {
		Integer userId = getUserId(token);
		if (userId == null) {
			return false;
		}
		return verify(token, userId, String.valueOf(userId));
	}

	/**
	 * 校验token是否正确
	 *
	 * @param token  密钥
	 * @param secret 用户的密码
	 * @return 是否正确
	 */
	public static boolean verify(String token, Integer userId, String secret) {
		try {
			Algorithm algorithm = Algorithm.HMAC256(secret);
			JWTVerifier verifier = JWT.require(algorithm).withClaim(USERID, userId).build();
			DecodedJWT jwt = verifier.verify(token);
			jwt.getToken();
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 获得token中的信息无需secret解密也能获得
	 *
	 * @return token中包含的用户名
	 */
	public static String getUsername(String token) {
		try {
			DecodedJWT jwt = JWT.decode(token);
			return jwt.getClaim(USERNAME).asString();
		} catch (JWTDecodeException e) {
			return null;
		}
	}

	/**
	 * 获取登陆用户ID
	 *
	 * @param token
	 * @return
	 */
	public static Integer getUserId(String token) {
		try {
			DecodedJWT jwt = JWT.decode(token);
			return  jwt.getClaim(USERID).asInt();
		} catch (JWTDecodeException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * @return
	 * @description: 从当前线程中获取用户信息
	 * @author: Vic.xu
	 * @date: 2020年2月17日 下午4:28:13
	 */
	public static AuthorityInfo getAuthorityInfoFromShiro() {
		String token = (String) SecurityUtils.getSubject().getPrincipal();
		if (StringUtils.isBlank(token)) {
			return null;
		}
		return getAuthorityInfo(token);
	}

	/**
	 * 获得token中的信息
	 *
	 * @return AuthorityInfo
	 */
	public static AuthorityInfo getAuthorityInfo(String token) {
		try {
			DecodedJWT jwt = JWT.decode(token);
			String info = jwt.getClaim("info").asString();
			AuthorityInfo authorityInfo = objectMapper.readValue(info, AuthorityInfo.class);
			return authorityInfo;
		} catch (Exception e) {
			e.printStackTrace();
			return new AuthorityInfo();
		}

	}

	/**
	 * 以用户名作为签名的密码
	 */
	public static String sign(AuthorityInfo authorityInfo) {
		return sign(authorityInfo, authorityInfo.getUserId() + "");
	}

	/**
	 * 生成签名,X分钟后过期
	 *
	 * @param authorityInfo 用户信息
	 * @param secret        用户的密码
	 * @return 加密的token
	 */
	public static String sign(AuthorityInfo authorityInfo, String secret) {
		Assert.notNull(authorityInfo, "用户信息不可为空");
		try {
			// 开始检测是否需要更换token的时机
//			long expireCheckTime = System.currentTimeMillis() + EXPIRE_CHECK;
			Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
//			String info = objectMapper.writeValueAsString(authorityInfo);
			Algorithm algorithm = Algorithm.HMAC256(secret);
			// 附带相关信息
			return JWT.create().withClaim(USERID, authorityInfo.getUserId())
					.withClaim(USERNAME, authorityInfo.getUsername())
					/*
					 * .withClaim("expireCheckTime", expireCheckTime) .withClaim("info", info)
					 */
					.withExpiresAt(date).sign(algorithm);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	

	/**
	 * 根据userId生成token
	 *
	 * @param userId
	 * @return
	 */
	public static String sign(String userId, String userName) {
		try {
			// 开始检测是否需要更换token的时机
			long expireCheckTime = System.currentTimeMillis() + EXPIRE_CHECK;

			Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
			Algorithm algorithm = Algorithm.HMAC256(userId);
			// 附带相关信息
			return JWT.create().withClaim(USERID, userId).withClaim(USERNAME, userName)
					.withClaim("expireCheckTime", expireCheckTime).withExpiresAt(date).sign(algorithm);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

}
